package main

import (
	"fmt"
)

/*
channel 是用于 goroutine之间的通信
单纯的将函数进行并发执行是没有意义的，函数与函数需要交换数据才能体现并发执行函数的意义。

虽然可以通过共享内存进行数据交换，但是共享内存在不同的goroutine 中容易发生竞态问题
为了保证数据交换的正确性，必须使用互斥量对内存进行加锁，这种做法势必会造成性能问题。

GO的并发编程模型CSP （Communicating Sequential Progress）,提倡通过通信共享内存，
而并不是通过共享内存而实现通信。

如果说 goroutine 是Go程序并发的执行体，channel 就是他们之间的连接，
channel 是可以让一个goroutine发送特定值到另一个goroutine的通信机制。

Go 语言中的通道，（channel） 是一种特殊的类型，通道像一个传动带或者队列，
总是遵循先入先出的规则，保证收发数据的顺序，每一个通道都是一个具体类型的导管，
也就是声明 channel 的时候需要为其指定所传输的元素类型
*/

/*
channel是一种数据类型
channel是一种引用类型，声明通道类型的格式如下（用chan 关键字）

	var 变量名 chan 元素类型

变量名是channel 变量名，元素类型应该是 channel 传输的数据类型。

	var ch1 chan int    //声明一个传递整型的通道ch1
	var ch2 chan bool   //声明一个传递布尔类型的通道ch2
	var ch2 chan bool   //声明一个传递int切片的通道ch3
*/
func main() {

	//三创建channel
	//1.声明channel(通道是引用数类型，通道类型的空值是nil)
	var ch1 chan int
	fmt.Println(ch1) //<nil>
	//只声明通道是无法使用通道的

	//2.一般使用make 一步完成声明与创建
	//通道需要使用make 函数初始化之后才能使用。
	//使用make 函数创建channel的格式如下：

	//make(chan 元素类型，[缓冲大小])

	//channel的缓冲大小是可选的

	//make函数的使用举例

	//ch2:=make(chan int)
	//ch3:=make(chan bool)
	//ch4:=make(chan []int)

	ch2 := make(chan int)
	fmt.Println(ch2) //0xc00001a0c0

	//四channel操作
	//通道有 发送（send） 接收(receive) 和 关闭(close) 三种操作
	//发送和接收都使用 <- 符号
	//创建通道
	//ch3 := make(chan int)
	//ch3 <- 8//将一个值发送到通道中
	//x := <-ch3//从一个通道中接收值
	//close(ch3)//关闭通道（我们调用内置的close函数来关闭通道）
	//fmt.Println(x)
	/*
		注意：
		这里的关闭不是完全关闭，只是针对发送方来说通道关闭，也就是说不能再向通道中发送值，但是仍然可以从中接收剩余值

		关于通道需要注意的事情是，只有在通知接收方goroutine 所有的数据都发送完毕的时候才需要关闭通道。
		通道是可以被垃圾回收机制回收的，他和关闭文件是不一样的，在结束操作之后关闭文件是必须要做的，但关闭通道不是必须的。

		关闭后的通道有一下特点：
		1.对一个关闭的通道在发送值就会导致 panic
		2.对一个关闭的通道进行接收会一直获取值直到通道为空。
		3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的0值。
		4.关闭一个已经关闭的通道会导致 panic
	*/

	//五,无缓冲的通道
	//无缓冲的通道又称为阻塞式通道
	//例如:
	//ch := make(chan int)
	//ch <- 10   //往通道发送一个值10
	//fmt.Println("发送成功")
	//上面这段代码会报错
	/*
		fatal error: all goroutines are asleep - deadlock!

		goroutine 1 [chan send]:
		main.main()
			E:/Project/Golang/test/9992_Channel/demo.go:66 +0xc5
	*/
	//上面的代码会阻塞在 ch:=<-10 这一行代码形成死锁。

	/*
		这是因为我们使用ch:=make(chan int)  创建的是无缓冲的通道，只有在有人接收的时候才能发送值。
		就像你家小区没有快递柜和代收点，快递员给你打电话必须要将这个物品送到你的手上，
		简单上来说就是无缓冲的通道必须要有接收才能发送
	*/

	//如何解决？
	//一种方法是启用一个goroutine 去接收值
	ch := make(chan int)
	go recv(ch) // 启用goroutine从通道接收值
	ch <- 10
	fmt.Println("发送成功")
	/*
		无缓冲的通道上发送操作会阻塞，直到另外一个goroutine 在该通道上执行接收操作，这时值才能发送成功，两个goroutine 将继续执行。
		相反，如果接收操作先执行，接收方的goroutine 将阻塞，直到另外一个goroutine在该通道上发送一个值。

		使用无缓冲的通道进行通信将导致发送和收的goroutine 同步化，因此无缓冲通道也称为同步通道。
		无缓冲区通道相当于容量为0的有缓冲区通道。
	*/

}
func recv(c chan int) { //从通道接收值的函数
	ret := <-c
	fmt.Println("接收成功", ret)
}

/**
如果你不想在往通道里存值或者取值的时候，记得关闭通道。

*/
